//
//  LYLocationConverter.swift
//  MeasureMould
//
//  Created by iOS on 2021/4/6.
//

import Foundation
import CoreLocation

fileprivate let RANGE_LON_MAX = 137.8347
fileprivate let RANGE_LON_MIN = 72.004
fileprivate let RANGE_LAT_MAX = 55.8271
fileprivate let RANGE_LAT_MIN = 0.8293

fileprivate let jzA = 6378245.0
fileprivate let jzEE = 0.00669342162296594323

fileprivate func LAT_OFFSET_0(x:Double, y:Double) -> Double {
    return -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(fabs(x))
}
fileprivate func LAT_OFFSET_1(x:Double) -> Double {
    return (20.0 * sin(6.0 * x * .pi) + 20.0 * sin(2.0 * x * .pi)) * 2.0 / 3.0
}
fileprivate func LAT_OFFSET_2(y:Double) -> Double {
    return (20.0 * sin(y * Double.pi) + 40.0 * sin(y / 3.0 * .pi)) * 2.0 / 3.0
}
fileprivate func LAT_OFFSET_3(y:Double) -> Double {
    return (160.0 * sin(y / 12.0 * .pi) + 320 * sin(y * .pi / 30.0)) * 2.0 / 3.0
}

fileprivate func LON_OFFSET_0(x:Double, y:Double) -> Double {
    return 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(fabs(x))
}
fileprivate func LON_OFFSET_1(x:Double) -> Double {
    return (20.0 * sin(6.0 * x * .pi) + 20.0 * sin(2.0 * x * .pi)) * 2.0 / 3.0
}
fileprivate func LON_OFFSET_2(x:Double) -> Double {
    return (20.0 * sin(x * .pi) + 40.0 * sin(x / 3.0 * .pi)) * 2.0 / 3.0
}
fileprivate func LON_OFFSET_3(x:Double) -> Double {
    return (150.0 * sin(x / 12.0 * .pi) + 300.0 * sin(x / 30.0 * .pi)) * 2.0 / 3.0
}

class LocationConverter: NSObject {
    private class func transformLat(x:Double, y:Double) -> Double{
        var ret = LAT_OFFSET_0(x: x, y: y)
        ret += LAT_OFFSET_1(x: x)
        ret += LAT_OFFSET_2(y: y)
        ret += LAT_OFFSET_3(y: y)
        return ret
    }

    private class func transformLon(x:Double, y:Double) -> Double{
        var ret = LON_OFFSET_0(x: x, y: y)
        ret += LON_OFFSET_1(x: x)
        ret += LON_OFFSET_2(x: x)
        ret += LON_OFFSET_3(x: x)
        return ret
    }

    private class func outOfChina(lat:Double, lon:Double) -> Bool{
        if lon < RANGE_LON_MIN || lon > RANGE_LON_MAX {
            return true
        }
        if lat < RANGE_LAT_MIN || lat > RANGE_LAT_MAX {
            return true
        }
        return false
    }


    /// GPS WGS84坐标转火星坐标
    /// - Parameters:
    ///   - gglat: 纬度
    ///   - gglon: 经度
    /// - Returns: GCJ02 火星经纬度
    public class func gcj02Encrypt(gglat:Double, gglon:Double) -> CLLocationCoordinate2D {
        var resPoint = CLLocationCoordinate2D()
        var mgLat:Double = 0
        var mgLon:Double = 0
        if self.outOfChina(lat: gglat, lon: gglon) {
            resPoint.latitude = gglat
            resPoint.longitude = gglon
            return resPoint
        }
        var dLat = self.transformLat(x: gglon - 105.0, y: gglat - 35.0)
        var dLon = self.transformLon(x: gglon - 105.0, y: gglat - 35.0)
        let radLat = gglat / 180.0 * .pi
        var magic = sin(radLat)
        magic = 1 - jzEE * magic * magic
        let sqrtMagic = sqrt(magic)
        dLat = (dLat * 180.0) / ((jzA * (1 - jzEE)) / (magic * sqrtMagic) * Double.pi)
        dLon = (dLon * 180.0) / (jzA / sqrtMagic * cos(radLat) * Double.pi)
        mgLat = gglat + dLat
        mgLon = gglon + dLon
        resPoint.latitude = mgLat
        resPoint.longitude = mgLon
        return resPoint
    }


    /// GCJ02  坐标转 DB09 坐标系
    /// - Parameter coor: 经纬度
    /// - Returns: 转换过的经纬度
    class func GCJ02FromBD09(coor:CLLocationCoordinate2D) -> CLLocationCoordinate2D {
        let x_pi:CLLocationDegrees = 3.14159265358979324 * 3000.0 / 180.0
        let x:CLLocationDegrees = coor.longitude - 0.0065, y = coor.latitude - 0.006
        let z:CLLocationDegrees = sqrt(x*x + y*y) - 0.00002 * sin(y * x_pi)
        let theta:CLLocationDegrees = atan2(y, x) - 0.00003 * cos(x * x_pi)
        let gg_long:CLLocationDegrees = z * cos(theta)
        let gg_lat:CLLocationDegrees = z * sin(theta)

        return CLLocationCoordinate2DMake(gg_lat, gg_long)
    }

    class func getLatLon(_ dd:Int, mm:Int, ss:Int, sss:Int) -> Double {
        let number = (Double(dd) * 3600000 + Double(mm) * 60000 + Double(ss) * 10 + Double(sss) / 10) / 1000
        return number / 3600
    }

    /// 度转度分秒
    /// - Parameter coordinate: 度
    /// - Returns: 度分秒
    func stringWithCoordinater(coordinate:String) -> String {
        let latNumber:Int = Int(Double(coordinate)!)
        let tp = (Double(coordinate)! - Double(latNumber)) * 60
        let fen = Int(tp)
        let miao = abs((tp - Double(fen)) * 60)
        let str:String = String(latNumber) + "°" + String(abs(fen)) + "'" + String(format: "%.2f", miao) + "\""
        return str
    }

    /// 1平米  = 0.0015亩
    func calculateArea(points:[CLLocationCoordinate2D]) -> Double {
        if points.count < 3 {
            return 0
        }
        var area0:Double = 0
        var area1:Double = 0
        let count = points.count
        for index in 0 ..< count {
            let x = points[index].longitude
            let y = index + 1 < count ? points[index + 1].latitude : points[0].latitude
            area0 += x * y

            let lat = points[index].latitude
            let lon = index + 1 < count ? points[index + 1].longitude : points[0].longitude
            area1 += lat * lon
        }
        round(2)
        let S = fabs(0.5 * (area0 - area1)).roundTo(places: 2) * 9101160000.085981
        return S
    }

    /// 计算弧度
    func convertToradian(input:Double) -> Double {
        return Double(input) * .pi / 180
    }
    func calculatePolygonArea(coords:[CLLocationCoordinate2D]) -> Double {
        var area:Double = 0
        let lenth = coords.count
        if lenth < 3 {
            return 0
        }
        for i in 0 ..< lenth {
            let lon = i + 1 < lenth ? coords[i + 1].longitude : coords[0].longitude
            let lat = i + 1 < lenth ? coords[i + 1].latitude : coords[0].latitude
            area += convertToradian(input: (lon - coords[i].longitude)) * (2 + sin(convertToradian(input: coords[i].latitude)) + sin(convertToradian(input: lat)))
        }
        area = area * 6378137.0 * 6378137.0 / 2.0
        return fabs(area).roundTo(places: 6)
    }


}

